/*    
 TCSS 458 Spring 2012   Project 3 Milestone 1 Solution

 This solution is based on the work of 458 Students
 Anthony Bisig (most) and Thach Nyugen (Bresenham & Scanline code)
 as edited and merged by the instructor, John Mayer.

 Could use some more tidying up, but it should be useful
 for anyone whose Milestone 1 is seriously broken.
 */

#include <stdlib.h>
#include "GL/glut.h"

#include <stdio.h>
#include <fstream>
#include <iostream>
#include <string.h>
#include <math.h>

#define WINDOW_WIDTH 600
#define WINDOW_HEIGHT 600

FILE *input;
using namespace std;

typedef GLfloat Matrix4x4[4][4];

Matrix4x4 modelview;
Matrix4x4 projection;
Matrix4x4 *cMat = &modelview; // initialize currentMatrix to modelview

GLfloat z_buffer[WINDOW_HEIGHT][WINDOW_HEIGHT];

class wcPt3D {
public:
  GLfloat x, y, z, w;
};

class point3d {
public:
  GLfloat x, y, z;
};

class screenPt {

public:
  GLfloat x, y;
  screenPt() {
    x = y = -1;
  }
  void setCoords(GLfloat xCoordValue, GLfloat yCoordValue) {
    x = xCoordValue;
    y = yCoordValue;
  }

  void setX(GLfloat xCoordValue) {
    x = xCoordValue;
  }

  void setY(GLfloat yCoordValue) {
    y = yCoordValue;
  }

  GLfloat getx() const {
    return x;
  }

  GLfloat gety() const {
    return y;
  }
};

const int INT_MAX = 0b01111111111111111111111111111111;

class scanLine {
private:
  int minx[2000]; // Resizing window past 2000 high is a problem, not bothering to program for it though...
  int maxx[2000];
  int myheight;
  int myz[1000][1000];

public:
  scanLine(int height) {
    myheight = height;
    for (int i = 0; i < myheight; i++) {
      minx[i] = -INT_MAX;
      maxx[i] = -INT_MAX;
    }
    for (int i = 0; i < 1000; i++) {
      for (int j = 0; j < 1000; j++) {
        myz[i][j] = -INT_MAX;
      }
    }
  }

  void setHeight(int height) {
    myheight = height;
    for (int i = 0; i < myheight; i++) {
      minx[i] = -INT_MAX;
      maxx[i] = -INT_MAX;
    }
  }

  void addIntersection(int x, int y, int z) {
    //printf("Adding Intersection (%d,%d,%d)\n", x, y, z);
    if (minx[y] == -INT_MAX) {
      minx[y] = x;
      maxx[y] = x;

    } else {
      // Figure out who is max, and swap 'em around
      if (x < minx[y]) { // Is this new X Smaller than the Min?
        minx[y] = x;
      } else if (x > maxx[y]) { // Is this new x Bigger than the max?
        maxx[y] = x;
      } // Else Ignore this X, it's inbetween the min/max range already
    }
    myz[y][x] = z;
  }

  int getminX(int y) {
    return minx[y];
  }

  int getmaxX(int y) {
    return maxx[y];
  }

  int getZ(int x, int y) {
    return myz[y][x];
  }

  int getHeight() {
    return myheight;
  }
  void clear() {
    for (int i = 0; i < myheight; i++) {
      minx[i] = -INT_MAX;
      maxx[i] = -INT_MAX;
    }
  }
};
//
//class scanLine {
//public:
//  GLfloat lines[WINDOW_HEIGHT][2][2];
//
//  scanLine() {
//    for (int i = 0; i < WINDOW_HEIGHT; i++) {
//      lines[i][0][0] = -1;
//      lines[i][0][1] =  pow(2,30) + 1;
//      lines[i][1][0] = -1;
//      lines[i][1][1] =  pow(2,30) + 1;
//    }
//  }
//  void addIntersection(GLfloat x, GLfloat y, GLfloat z) {
//    int ix = x;
//    int iy = y;
//    int iz = z;
//    if (0 <= ix && ix < WINDOW_HEIGHT) {
//      //none set yet, assing leftmost
//      if (lines[iy][0][0] == -1) {
//        lines[iy][0][0] = ix;
//        lines[iy][0][1] = iz;
//      } else if (ix < lines[iy][0][0] && lines[iy][1][0] == -1) {
//        //push leftmost over
//        lines[iy][1][0] = lines[iy][0][0];
//        lines[iy][1][1] = lines[iy][0][1];
//
//        lines[iy][0][0] = ix;
//        lines[iy][0][1] = iz;
//      } else if (ix < lines[iy][0][0] && lines[iy][1][0] >= 0) {
//        //replace leftmost
//        lines[iy][0][0] = ix;
//        lines[iy][0][1] = iz;
//      } else if (ix > lines[iy][1][0]) {
//        //replace rightmost
//        lines[iy][1][0] = ix;
//        lines[iy][1][1] = iz;
//      }
//
//    }
//  }
//};

scanLine* sl = new scanLine(WINDOW_HEIGHT);

/**
 * Draws a single pixel to the window.
 */
void setPixel(GLfloat xCoord, GLfloat yCoord) {
  glBegin(GL_POINTS);
  glVertex2i(xCoord, yCoord);
  glEnd();
}

struct Span {
  int min;
  int max;
};

Span *yScans[1000]; // max height of an image is now 1000

void setScan(int x, int y) {

  if (yScans[y] == NULL) {
    yScans[y] = new Span();
    yScans[y]->min = WINDOW_WIDTH;
    yScans[y]->max = -1;
  }
  if (yScans[y]->max < x)
    yScans[y]->max = x;
  if (x < yScans[y]->min)
    yScans[y]->min = x;

}

/*  Modified Bresenham line-drawing procedure for |m| < 1.0.  
 If last param is true, then the y-scanline data structure
 will be used in preparation for filling a polygon.
 */
void lineBres(int x0, int y0, int xEnd, int yEnd, int fill) {
  int dx = abs(xEnd - x0), dy = abs(yEnd - y0);
  int x, y, step = 1;
  int p = 2 * dy - dx;
  int twoDy = 2 * dy, twoDyMinusDx = 2 * (dy - dx);
  int twoDx = 2 * dx, twoDxMinusDy = 2 * (dx - dy);

  if (dx > dy) {
    /* Determine which endpoint to use as start position.  */
    if (x0 > xEnd) {
      x = xEnd;
      y = yEnd;
      xEnd = x0;
      yEnd = y0;
    } else {
      x = x0;
      y = y0;
    }
    setPixel(x, y);
    if (fill)
      setScan(x, y);

    // determine whether there is a negative slope
    if (y > yEnd) {
      step = -step;
    }
    while (x < xEnd) {
      x++;
      if (p < 0)
        p += twoDy;
      else {
        y += step;
        p += twoDyMinusDx;
      }
      setPixel(x, y);
      if (fill)
        setScan(x, y);
    }
  }
  //Reverse roles if dy > dx
  else {
    p = 2 * dx - dy;
    /* Determine which endpoint to use as start position.  */
    if (y0 > yEnd) {
      x = xEnd;
      y = yEnd;
      xEnd = x0;
      yEnd = y0;
    } else {
      x = x0;
      y = y0;
    }
    setPixel(x, y);
    if (fill)
      setScan(x, y);

    // determine whether there is a negative slope
    if (x > xEnd) {
      step = -step;
    }
    while (y < yEnd) {
      y++;
      if (p < 0)
        p += twoDx;
      else {
        x += step;
        p += twoDxMinusDy;
      }
      setPixel(x, y);
      if (fill)
        setScan(x, y);
    }
  }
}

void tryPixel(GLfloat x, GLfloat y, GLfloat z) {
  ////printf("Trying pixel (%.3d,%.3d,%.3d)\n", (int)x,(int)y,(int)z);
  //fflush(stdout);
  int ix = x;
  int iy = y;

  if (ix >= 0 && ix < WINDOW_WIDTH && iy >= 0 && iy < WINDOW_HEIGHT
      && z < z_buffer[iy][ix]) {
    //printf("(%d,%d) -- oldz: %.3f, newz: %.3f\n", ix, iy, z_buffer[iy][ix], z);
    z_buffer[iy][ix] = z;
    setPixel(x, y);
  }
}

void bresenham_line_3D(int x1, int y1, int z1, int x2, int y2, int z2,
    bool fill, scanLine* asl) {
  int pixel[3];
  int dx, dy, dz, l, y_inc, x_inc, z_inc, n, m, dx2, dy2, dz2, err_1, err_2, i;
  pixel[0] = x1;
  pixel[1] = y1;
  pixel[2] = z1;
  dx = x2 - x1;
  dy = y2 - y1;
  dz = z2 - z1;
  x_inc = (dx < 0) ? -1 : 1;
  l = abs(dx);
  y_inc = (dy < 0) ? -1 : 1;
  m = abs(dy);
  z_inc = (dz < 0) ? -1 : 1;
  n = abs(dz);
  dx2 = l * 2;
  dy2 = m * 2;
  dz2 = n * 2;

  if ((l >= m) && (l >= n)) {
    err_1 = dy2 - l;
    err_2 = dz2 - l;
    for (i = 0; i < l; i++) {

      tryPixel(pixel[0], pixel[1], pixel[2]);
      if (fill) {
        asl->addIntersection(pixel[0], pixel[1], pixel[2]);
      }

      if (err_1 > 0) {
        pixel[1] += y_inc;
        err_1 -= dx2;
      }
      if (err_2 > 0) {
        pixel[2] += z_inc;
        err_2 -= dx2;
      }
      err_1 += dy2;
      err_2 += dz2;
      pixel[0] += x_inc;
    }
  } else if ((m >= l) && (m >= n)) {
    err_1 = dx2 - m;
    err_2 = dz2 - m;
    for (i = 0; i < m; i++) {
      //printf("Trying pixel (%.3d,%.3d,%.3d)\n", pixel[0], pixel[1], pixel[2]);
      //fflush(stdout);
      tryPixel(pixel[0], pixel[1], pixel[2]);
      if (fill) {
        asl->addIntersection(pixel[0], pixel[1], pixel[2]);
      }

      if (err_1 > 0) {
        pixel[0] += x_inc;
        err_1 -= dy2;
      }
      if (err_2 > 0) {
        pixel[2] += z_inc;
        err_2 -= dy2;
      }
      err_1 += dx2;
      err_2 += dz2;
      pixel[1] += y_inc;
    }
  } else {
    err_1 = dy2 - n;
    err_2 = dx2 - n;
    for (i = 0; i < n; i++) {
      //printf("Trying pixel (%.3d,%.3d,%.3d)\n", pixel[0], pixel[1], pixel[2]);
      //fflush(stdout);
      tryPixel(pixel[0], pixel[1], pixel[2]);
      if (fill) {
        asl->addIntersection(pixel[0], pixel[1], pixel[2]);
      }

      if (err_1 > 0) {
        pixel[1] += y_inc;
        err_1 -= dz2;
      }
      if (err_2 > 0) {
        pixel[0] += x_inc;
        err_2 -= dz2;
      }
      err_1 += dy2;
      err_2 += dx2;
      pixel[2] += z_inc;
    }
  }
  //printf("Trying pixel (%.3d,%.3d,%.3d)\n", pixel[0], pixel[1], pixel[2]);
  //fflush(stdout);
  tryPixel(pixel[0], pixel[1], pixel[2]);
  if (fill) {
    asl->addIntersection(pixel[0], pixel[1], pixel[2]);
  }
}

void polygon(int maxVert, screenPt *vertices) {
  int i;
  int count = 0;
  for (i = 0; i < maxVert - 1; i++) {
    lineBres(vertices[i].x, vertices[i].y, vertices[i + 1].x, vertices[i + 1].y,
        true);
    count++;
  }
  lineBres(vertices[i].x, vertices[i].y, vertices[0].x, vertices[0].y, true);

  count++;

  for (int y = 0; y < WINDOW_HEIGHT; y++) {
    if (yScans[y] != NULL) {
      lineBres(yScans[y]->min, y, yScans[y]->max, y, false);
    }
  }

  for (int y = 0; y < WINDOW_HEIGHT; y++) {
    free(yScans[y]);
    yScans[y] = NULL;
  }

}

void printMatrix(Matrix4x4 m) {
  GLint row, col;

  for (row = 0; row < 4; row++) {
    for (col = 0; col < 4; col++) {
      //printf("%f ", m[row][col]);
    }
    //printf("\n");
  }

}

/**
 * Fills a 4x4 matrix with zeros.
 */
void clearMatrix(Matrix4x4 m) {
  GLint row, col;

  for (row = 0; row < 4; row++) {
    for (col = 0; col < 4; col++) {
      m[row][col] = 0;
    }
  }
}

/**
 * Sets a matrix to the identity matrix.
 */
void matrix4x4SetIdentity(Matrix4x4 matIdent4x4) {

  GLint row, col;

  for (row = 0; row < 4; row++) {
    for (col = 0; col < 4; col++) {
      matIdent4x4[row][col] = (row == col);
    }
  }
}

/**
 * Copys a matrix's values into another.
 */
void matrixCopy(Matrix4x4 temp, Matrix4x4 empty) {

  GLint row, col;

  for (row = 0; row < 4; row++) {
    for (col = 0; col < 4; col++) {
      empty[row][col] = temp[row][col];
    }
  }
}

/**
 * Swaps the values between two matrices.
 */
void matrixSwap(Matrix4x4 m1, Matrix4x4 m2) {

  Matrix4x4 temp;

  matrixCopy(m1, temp);
  matrixCopy(m2, m1);
  matrixCopy(temp, m2);
}

/**
 * Multplies two matrices together using the rows of the second matrix and the
 * columns of the first.
 */
void postMultiply(Matrix4x4 m1, Matrix4x4 m2) { // calculates m1 * m2 and leaves answer in m1

  GLint row, col;
  Matrix4x4 matTemp;

  for (row = 0; row < 4; row++) {
    for (col = 0; col < 4; col++) {
      matTemp[row][col] = (m1[row][0] * m2[0][col]) + (m1[row][1] * m2[1][col])
          + (m1[row][2] * m2[2][col]) + (m1[row][3] * m2[3][col]);
    }
  }

  for (row = 0; row < 4; row++) {
    for (col = 0; col < 4; col++) {
      m1[row][col] = matTemp[row][col];
    }
  }
}

wcPt3D matrPointMultiply(Matrix4x4 m1, wcPt3D vert) { // calculates m1 * m2 and leaves answer in m1

  GLint row, col;
  Matrix4x4 matTemp;
  wcPt3D newVert;

  newVert.x = m1[0][0] * vert.x + m1[0][1] * vert.y + m1[0][2] * vert.z
      + m1[0][3] * vert.w;
  newVert.y = m1[1][0] * vert.x + m1[1][1] * vert.y + m1[1][2] * vert.z
      + m1[1][3] * vert.w;
  newVert.z = m1[2][0] * vert.x + m1[2][1] * vert.y + m1[2][2] * vert.z
      + m1[2][3] * vert.w;
  newVert.w = m1[3][0] * vert.x + m1[3][1] * vert.y + m1[3][2] * vert.z
      + m1[3][3] * vert.w;

  return newVert;
}

/**
 * Sets up a translation matrix to be mutliplied with the current matrix.
 */
void translate3d(GLfloat tx, GLfloat ty, GLfloat tz) {

  Matrix4x4 matTransl3d;

  matrix4x4SetIdentity(matTransl3d);

  matTransl3d[0][3] = tx;
  matTransl3d[1][3] = ty;
  matTransl3d[2][3] = tz;

  postMultiply(*cMat, matTransl3d);
}

/**
 * Sets up a scaling matrix to be multiplied with the current matrix.
 */
void scale3d(GLfloat sx, GLfloat sy, GLfloat sz) {
  Matrix4x4 matScale3d;

  matrix4x4SetIdentity(matScale3d);

  matScale3d[0][0] = sx;
  matScale3d[1][1] = sy;
  matScale3d[2][2] = sz;

  postMultiply(*cMat, matScale3d);
}

/*  Procedure for generating a quaternion rotation matrix.  */
void rotate3D(wcPt3D p2, GLfloat radianAngle) {
  wcPt3D p1;
  Matrix4x4 matQuatRot;

  p1.x = 0;
  p1.y = 0;
  p1.z = 0;

  float axisVectLength = sqrt(
      (p2.x - p1.x) * (p2.x - p1.x) + (p2.y - p1.y) * (p2.y - p1.y)
          + (p2.z - p1.z) * (p2.z - p1.z));
  float cosA = cosf(radianAngle);
  float oneC = 1 - cosA;
  float sinA = sinf(radianAngle);
  float ux = (p2.x - p1.x) / axisVectLength;
  float uy = (p2.y - p1.y) / axisVectLength;
  float uz = (p2.z - p1.z) / axisVectLength;

  /*  Set up translation matrix for moving p1 to origin,
   *  and concatenate translation matrix with matComposite.
   // not needed - openGL p1 is already at Origin

   /*  Initialize matQuatRot to identity matrix.  */
  matrix4x4SetIdentity(matQuatRot);

  matQuatRot[0][0] = ux * ux * oneC + cosA;
  matQuatRot[0][1] = ux * uy * oneC - uz * sinA;
  matQuatRot[0][2] = ux * uz * oneC + uy * sinA;
  matQuatRot[1][0] = uy * ux * oneC + uz * sinA;
  matQuatRot[1][1] = uy * uy * oneC + cosA;
  matQuatRot[1][2] = uy * uz * oneC - ux * sinA;
  matQuatRot[2][0] = uz * ux * oneC - uy * sinA;
  matQuatRot[2][1] = uz * uy * oneC + ux * sinA;
  matQuatRot[2][2] = uz * uz * oneC + cosA;

  /*  Concatenate matQuatRot with composite matrix.  */
  postMultiply(*cMat, matQuatRot);

  /*  Construct inverse translation matrix for p1 and
   *  concatenate with composite matrix.
   */
  // not needed - see note above.
}

/**
 * Checks with a vertex's point is outside of the clipping cube.
 * If so then the program prints a message and exits.
 */
void checkCube(float x, float y, float z) {
  int pass = 1;
  if (x < -1 || x > 1) {
    pass = 0;

  } else if (y < -1 || y > 1) {
    pass = 0;

  } else if (z < -1 || z > 1) {
    pass = 0;

  }

  if (pass == 0) {
    //printf("Vertex is outside the clipping cube. Please try again.");
    exit(1);
  }

}

wcPt3D vertexToPixelCoord(wcPt3D vert, float xMin, float xMax, float yMin,
    float yMax) {
  vert = matrPointMultiply(modelview, vert); /// apply modelview matrix
  vert = matrPointMultiply(projection, vert); //  apply projection

  vert.x /= vert.w; // leave homogeneous coordinates behind
  vert.y /= vert.w;
  vert.z /= vert.w;
  vert.w = 1;

  checkCube(vert.x, vert.y, vert.z); // now evertying should be in clipping cube

  vert.x *= (xMax - xMin) / 2; // map to viewport
  vert.y *= (yMax - yMin) / 2;
  vert.x += (xMax - xMin) / 2 + xMin;
  vert.y += (yMax - yMin) / 2 + yMin;
  return vert;
}

void calcDrawColor(GLfloat* mat_color, GLfloat* light,
    GLfloat* normal) {
  float draw_color[3];

  float l_light = sqrt(
      (light[0] * light[0]) + (light[1] * light[1]) + (light[2] * light[2]));
  float n_light[] =
      { light[0] / l_light, light[1] / l_light, light[2] / l_light };

  float l_normal = sqrt(
      (normal[0] * normal[0]) + (normal[1] * normal[1])
          + (normal[2] * normal[2]));
  float n_normal[] = { normal[0] / l_normal, normal[1] / l_normal, normal[2]
      / l_normal };

  float scale = (n_light[0] * n_normal[0]) + (n_light[1] * n_normal[1])
      + (n_light[2] * n_normal[2]);
  scale = (scale + 1) / 2.0;
  draw_color[0] = scale * mat_color[0];
  draw_color[1] = scale * mat_color[1];
  draw_color[2] = scale * mat_color[2];

  glColor3f(draw_color[0], draw_color[1], draw_color[2]);
}

/**
 * Glut display function that draws shapes from a text file.
 * @author Anthony Bisig
 */
void drawFileShapes(void) {
  glClear(GL_COLOR_BUFFER_BIT);
  char word[20];
  wcPt3D first;
  first.x = first.y = -1;
  first.z = pow(2, 30) + 1;
  wcPt3D previous;
  previous.x = previous.y = -1;
  previous.z = pow(2, 30) + 1;
  GLfloat x;
  GLfloat y;
  GLfloat z;
  GLfloat w;

  GLfloat viewx;
  GLfloat viewy;

  Matrix4x4 mstack[32];
  Matrix4x4 matr;

  GLfloat xMin, xMax, yMin, yMax;
  GLfloat color[3];
  GLfloat mat_color[3];
  GLfloat draw_color[3];
  GLfloat clearColor[4];

  GLfloat normal[3];
  GLfloat light[3];

  wcPt3D vert;

  matrix4x4SetIdentity(mstack[0]);
  int index = 1;

  while (feof(input) == 0) {
    fscanf(input, "%s", word);
    if (strcmp(word, "glViewport") == 0) {
      float width, height;
      fscanf(input, "%f", &xMin);
      fscanf(input, "%f", &yMin);
      fscanf(input, "%f", &width);
      xMax = xMin + width;
      fscanf(input, "%f", &height);
      yMax = yMin + height;

    } else if (strcmp(word, "glMatrixMode") == 0) {
      fscanf(input, "%s", word);

      if (strcmp(word, "GL_PROJECTION") == 0) {
        cMat = &projection;

      } else if (strcmp(word, "GL_MODELVIEW") == 0) {
        cMat = &modelview;
      }

    } else if (strcmp(word, "glLoadIdentity") == 0) {
      matrix4x4SetIdentity(*cMat);

    } else if (strcmp(word, "glFrustum") == 0) {
      float le, r, b, t, n, f;
      fscanf(input, "%f", &le);
      fscanf(input, "%f", &r);
      fscanf(input, "%f", &b);
      fscanf(input, "%f", &t);
      fscanf(input, "%f", &n);
      fscanf(input, "%f", &f);

      clearMatrix(matr);

      matr[0][0] = ((2 * n) / (r - le));
      matr[0][2] = ((le + r) / (r - le));
      matr[1][1] = ((2 * n) / (t - b));
      matr[1][2] = ((t + b) / (t - b));
      matr[2][2] = (-(f + n) / (f - n));
      matr[2][3] = ((-2 * f * n) / (f - n));
      matr[3][2] = -1;

      postMultiply(*cMat, matr);

    } else if (strcmp(word, "glOrtho") == 0) {
      float le, r, b, t, n, f;
      fscanf(input, "%f", &le);
      fscanf(input, "%f", &r);
      fscanf(input, "%f", &b);
      fscanf(input, "%f", &t);
      fscanf(input, "%f", &n);
      fscanf(input, "%f", &f);

      clearMatrix(matr);

      matr[0][0] = (2 / (r - le));
      matr[0][3] = -((r + le) / (r - le));
      matr[1][1] = (2 / (t - b));
      matr[1][3] = -((t + b) / (t - b));
      matr[2][2] = (-2 / (f - n));
      matr[2][3] = (f + n) / (f - n);
      matr[3][3] = 1;

      postMultiply(*cMat, matr);
      printMatrix(*cMat);

    } else if (strcmp(word, "glClearColor") == 0) {
      for (int i = 0; i < 4; i++) {
        fscanf(input, "%f", &clearColor[i]);
      }
    } else if (strcmp(word, "glColor3f") == 0) {

      for (int i = 0; i < 3; i++) {
        fscanf(input, "%f", &color[i]);
      }
      glColor3f(color[0], color[1], color[2]);

    } else if (strcmp(word, "glClear") == 0) {
      glColor3f(clearColor[0], clearColor[1], clearColor[2]);
      for (int r = 0; r < WINDOW_HEIGHT; r++) {
        for (int c = 0; c < WINDOW_WIDTH; c++) {
          setPixel(r, c);
        }
      }
      glColor3f(color[0], color[1], color[2]);

    } else if (strcmp(word, "glPushMatrix") == 0) {

      matrixCopy(*cMat, mstack[index]);
      index++;

    } else if (strcmp(word, "glPopMatrix") == 0) {
      matrixCopy(mstack[index - 1], *cMat);
      index--;

    } else if (strcmp(word, "glTranslatef") == 0) {
      fscanf(input, "%f", &x);
      fscanf(input, "%f", &y);
      fscanf(input, "%f", &z);

      translate3d(x, y, z);

    } else if (strcmp(word, "glRotatef") == 0) {
      float angle;
      wcPt3D p2;
      fscanf(input, "%f", &angle);
      fscanf(input, "%f", &p2.x);
      fscanf(input, "%f", &p2.y);
      fscanf(input, "%f", &p2.z);

      rotate3D(p2, 3.14159 * angle / 180);

    } else if (strcmp(word, "glScalef") == 0) {
      fscanf(input, "%f", &x);
      fscanf(input, "%f", &y);
      fscanf(input, "%f", &z);

      scale3d(x, y, z);

    } else if (strcmp(word, "glBegin") == 0) {
      fscanf(input, "%s", word);
      fscanf(input, "%s", word);

      while (strcmp(word, "glEnd") != 0) {

        if (strcmp(word, "glVertex3f") == 0) {
          fscanf(input, "%f", &vert.x);
          fscanf(input, "%f", &vert.y);
          fscanf(input, "%f", &vert.z);
          vert.w = 1;

          if (previous.x == -1) {
            vert = vertexToPixelCoord(vert, xMin, xMax, yMin, yMax);
            first.x = vert.x;
            first.y = vert.y;
            first.z = vert.z;
            previous.x = vert.x;
            previous.y = vert.y;
            previous.z = vert.z;
          } else {
            //printf("Drawing line from (%d,%d,%d) to (%d,%d,%d)\n",(int)first.x, (int)first.y, (int)first.z * 1000, (int)previous.x,
            //(int)previous.y, (int)previous.z * 1000);
            vert = vertexToPixelCoord(vert, xMin, xMax, yMin, yMax);
            bresenham_line_3D(previous.x, previous.y, previous.z * 1000, vert.x,
                vert.y, vert.z * 1000, true, sl);
            previous.x = vert.x;
            previous.y = vert.y;
            previous.z = vert.z;
          }
        }
        fscanf(input, "%s", word);
      }

      if (previous.x != -1) {
        //printf("Drawing last line from (%d,%d,%d) to (%d,%d,%d)\n",(int)first.x, (int)first.y, (int)first.z * 1000, (int)previous.x,
        //(int)previous.y, (int)previous.z * 1000);
        bresenham_line_3D(first.x, first.y, first.z * 1000, previous.x,
            previous.y, previous.z * 1000, true, sl);
        //printf("Finished last line\n");
        previous.x = -1;
        previous.y = -1;
        previous.z = pow(2, 30) + 1;
      }

      //printf("About to fill\n");
      for (int l = 0; l < WINDOW_HEIGHT; l++) {
        int minX = sl->getminX(l);
        int maxX = sl->getmaxX(l);
        //printf("l=%d\n",l);

        if (minX > 0 && minX < WINDOW_HEIGHT && maxX > 0 && maxX < WINDOW_HEIGHT) {
          int y = l;
          int minZ = sl->getZ(minX, y);
          int maxZ = sl->getZ(maxX, y);

          //printf("Drawing scan line from (%d,%d,%d) to (%d,%d,%d)\n",minX, l, minZ * 1000, maxX, l, maxZ * 1000);
          bresenham_line_3D(minX, l, minZ * 1000, maxX, l, maxZ * 1000, false,
              sl);
        }
      }

      sl->clear();

    } else if (strcmp(word, "glMaterialfv") == 0) {
      fscanf(input, "%f", &mat_color[0]);
      fscanf(input, "%f", &mat_color[1]);
      fscanf(input, "%f", &mat_color[2]);
      calcDrawColor(mat_color, light, normal);
    } else if (strcmp(word, "lightDirection") == 0) {
      fscanf(input, "%f", &light[0]);
      fscanf(input, "%f", &light[1]);
      fscanf(input, "%f", &light[2]);
      calcDrawColor(mat_color, light, normal);
    } else if (strcmp(word, "glNormal3f") == 0) {
      fscanf(input, "%f", &normal[0]);
      fscanf(input, "%f", &normal[1]);
      fscanf(input, "%f", &normal[2]);
      calcDrawColor(mat_color, light, normal);
    } else {
      //printf("Breaking on %s\n", word);
      break;
    }
  }

  glFlush();
  fclose(input);
}

/**
 * Main method.
 */
int main(int argc, char** argv) {
  char fileName[30];

  int max = pow(2, 30);

//  for(int i=0;i<30;i++){
//    max = max << 1;
//    max++;
//  }

  for (int row = 0; row < WINDOW_HEIGHT; row++) {
    for (int col = 0; col < WINDOW_WIDTH; col++) {
      z_buffer[row][col] = max;
    }
  }

  glutInit(&argc, argv);
  glutInitDisplayMode(GLUT_SINGLE);
  glutInitWindowSize(WINDOW_WIDTH, WINDOW_HEIGHT);
  glutInitWindowPosition(100, 100);
  glutCreateWindow("JPGUNTER - Project3");
  glClearColor(1.0, 1.0, 1.0, 0.0);
  glClear(GL_COLOR_BUFFER_BIT);
  glMatrixMode(GL_PROJECTION); // Set projection parameters.
  gluOrtho2D(0.0, WINDOW_WIDTH, 0.0, WINDOW_HEIGHT);

  printf("Enter name of input file (no blanks): ");
  gets(fileName);
  //strcpy(fileName, "myt");
  input = fopen(fileName, "r+");
  if (input == NULL) {
    perror("Error opening file");

  } else {
    glutDisplayFunc(drawFileShapes);
  }
  glutMainLoop();
  return 0;
}
